package com.ahnchan.ContactsPlus.util;

import java.text.Collator;
import java.util.ArrayList;

import android.util.Log;

import com.ahnchan.ContactsPlus.Company;
import com.ahnchan.ContactsPlus.Contact;
import com.ahnchan.ContactsPlus.Group;
import com.ahnchan.ContactsPlus.Favorites;


/**
 * Utility for ArrayList to contained Object (Contact, Group, Company object)
 * - Sort (International)
 * - Merge
 * 
 * @author Ahnchan
 */
public class ListUtils {

	// Czech Language (use before 2011/5/2)
	static String[] czech = {"A","a","Á","á","B","b","C","c","Č","č","D","d","Ď","ď","E","e","É","é","Ě","ě","F","f",
							"G","g","H","h", "Ch","ch","I","i","Í","í","J","j","K","k","L","l","M","m","N","n","Ň","ň",
							"O","o","Ó","ó","P","p","Q","q","R","r","Ř","ř","S","s","Š","š","T","t","Ť","ť",
							"U","u","Ú","ú","Ů","ů","V","v","W","w","X","x","Y","y","Ý","ý","Z","z","Ž","ž"};

	// Swedish Language (use before 2011/5/2)
	static String[] swedish = {"A","a","B","b","C","c","D","d","E","e","F","f","G","g","H","h",
							"I","i","J","j","K","k","L","l","M","m","N","n","O","o","P","p","Q","q",
							"R","r","S","s","T","t","U","u","V","v","W","w","X","x","Y","y","Z","z",
							"Å","å","Ä","ä","Ö","ö"};

	/**
	 * Constructor
	 */
	public ListUtils() {
	}
	
	/**
	 * Sort ArrayList
	 * 
	 * @param list
	 * @param typeList
	 * @param language
	 * @param type
	 * @return
	 */
	static public ArrayList sortList(ArrayList list, int typeList, String language, String type) {

		ArrayList resultList = new ArrayList();
		
		ArrayList alphabetList = new ArrayList();
		ArrayList numberList = new ArrayList();
		ArrayList symbolList = new ArrayList();
		ArrayList localList = new ArrayList();
		
		// Change position Alphabet, number, symbol 
		for(int i=0; i < list.size(); i++) {
		
			char cFirst; 
		
			Object obj = list.get(i);
			if (null == obj) continue;
			
			String strName = getName(obj, type);

			// Some data is null
			if (null == strName) continue;
			if ("".equals(strName)) continue;
		
			try {
				cFirst = strName.trim().toUpperCase().charAt(0);
			} catch (Exception ex) {
				Log.e("ListUtils", type+","+ex.toString());
				cFirst = ' ';
			}
		
			// Debug Code
			if (cFirst == ' ') {
				Log.d("ListUtils", "i="+i + "," + cFirst +"," + strName);
			}
			
//			Log.d("ListUtils", ""+i+":"+cFirst+","+strName.substring(0).hashCode()+","+strName);
			
			// Sort
			if ((cFirst >= '0') && (cFirst <= '9')) {
				numberList.add(obj);
			} else if (cFirst < 'A') {
				symbolList.add(obj);
			} else {
				if ("ko".equals(language)) {
					if (((cFirst >= 0x1100) && (cFirst <= 0x11FF)) || 
							((cFirst >= 0xAC00) && (cFirst <= 0xD7AF))	) {
						localList.add(obj);
					} else {
						alphabetList.add(obj);
					}
				} else {
					alphabetList.add(obj);
				}
			}
		}

		Log.d("ContactsPlus", "[ListUtils]Parameter: TypeList:"+typeList+",Type:"+type+",Language:"+language);
		Log.d("ContactsPlus", "[ListUtils]List Count : Number:" + numberList.size()+",Symbol:"+symbolList.size()+",Alphabet:"+alphabetList.size()+",Local:"+localList.size());
		
		if ("Contact".equals(type)) {
			// Bubble sort (20110425) -> very very slow.
//			alphabetList = sortListInternational_bubble(alphabetList, language, type);
//			localList = sortListInternational_bubble(localList, language, type);

			// Quick sort (20110502) -> slow.
//			alphabetList = sortListInternational_quick(alphabetList, language, type, 0, alphabetList.size()-1);
//			localList = sortListInternational_quick(localList, language, type, 0, localList.size()-1);

			// Sort by ContactsPlus (pattern : ABCZABCDEFGZ)
			alphabetList = sortListInternational_two(alphabetList, language, type); // 2011/5/9
			localList = sortListInternational_two(localList, language, type); // 2011/5/9
			
			// First version (Not international)
//			alphabetList = sortListInternational_OLD(alphabetList, language, type); // Old Version 2011/5/2
//			localList = sortListInternational_OLD(localList, language, type); // Old Version 2011/5/2
		} else if ("Favorites".equals(type)) {

			// Sort by ContactsPlus (pattern : ABCZABCDEFGZ)
			alphabetList = sortListInternational_two(alphabetList, language, type); // 2011/5/9
			localList = sortListInternational_two(localList, language, type); // 2011/5/9

		}
		
		// Localization (to KOREAN: Korean first view) 
		if (localList.size() > 0) {
			localList.addAll(alphabetList);
			alphabetList = localList;
		}
		
		// Merge List
		if (typeList == 1) {
			resultList.addAll(alphabetList);
			resultList.addAll(numberList);
			resultList.addAll(symbolList);
		} else if (typeList == 2) {
			resultList.addAll(symbolList);
			resultList.addAll(numberList);
			resultList.addAll(alphabetList);
		} else if (typeList == 3) {
			resultList.addAll(numberList);
			resultList.addAll(symbolList);
			resultList.addAll(alphabetList);
		} else {
			resultList.addAll(alphabetList);
			resultList.addAll(numberList);
			resultList.addAll(symbolList);
		}
		
		return resultList;
	}
	
	/**
	 * merge three ArrayList
	 * 
	 * @param orgList All data list
	 * @param firstList Initial search result list
	 * @param secondList Memo, Phone, E-Mail search result list
	 * @param type
	 * @return
	 */
	static public ArrayList mergeList(ArrayList orgList, ArrayList firstList, ArrayList secondList, String type) {
		
		ArrayList resultList = new ArrayList();
		
		// TODO : upgrade more fast... (Commneted by Ahnchan 20110614)
		
		orgList = checkList(orgList, firstList, type);
		orgList = checkList_2(orgList, secondList, type);
		
		for (int i=0; i<orgList.size(); i++) {
			if (getShow(orgList.get(i), type)) {
				resultList.add(orgList.get(i));
			}
		}
		
		return resultList;
	}
	
	/**
	 * Compare two ArrayList. to resort ArrayList, to merge ArrayList  
	 *   
	 * it makes before made advanced international sorting function.(sortListInternational())  
	 * TODO : more think to change using sortListInternationcal()
	 * 
	 * @param orgList
	 * @param dataList
	 * @param type
	 * @return
	 */
	static private ArrayList checkList(ArrayList orgList, ArrayList dataList, String type) {
		
		// This logic has problem.
		// Not linear data don't marking.
		
		ArrayList resultList = new ArrayList();

		int orgCnt = 0;
		int dataCnt = 0;
		
		for(;;) {
			if (orgCnt >= orgList.size() || dataCnt >= dataList.size()) {
				break;
			}
			
			if (getID(orgList.get(orgCnt), type).equals(getID(dataList.get(dataCnt), type))) {
				setShow(orgList, orgCnt, true, type);
				dataCnt++;
			} else {
				orgCnt++;				
			}
		}		
		
		return orgList;
	}

	static private ArrayList checkList_2(ArrayList orgList, ArrayList dataList, String type) {
		
		for(int i=0; i<dataList.size(); i++) {
			for(int j=0; j<orgList.size(); j++) {
				if (getID(orgList.get(j), type).equals(getID(dataList.get(i), type))) {
					setShow(orgList, j, true, type);
					break;
				}
			}
		}
		
		return orgList;
	}

	/**
	 * Debug show value (write debug value)
	 * 
	 * @param list
	 * @param type
	 */
	static private void debugShow(ArrayList list, String type) {
		int iShow = 0;
		
		for(int i=0; i<list.size(); i++) {
			if (getShow(list.get(i), type)) {
				iShow++;
			}
		}
		
		Log.d("ContactsPlus", "[ListUtils] show : " + iShow);
	}
	
	/**
	 * set all object show properties
	 * 
	 * @param list
	 * @param show show property value
	 * @param type
	 * @return
	 */
	static public ArrayList setShowAll(ArrayList list, boolean show, String type) {
		
		for(int i=0; i<list.size(); i++) {
			setShow(list, i, show, type);
		}
		
		return list;
	}
	
	/**
	 * return object name
	 * 
	 * @param obj
	 * @param type
	 * @return
	 */
	static private String getName(Object obj, String type) {
		
		String strResult = "";
	
		if ("Contact".equals(type)) {
			strResult = ((Contact)obj).getName();
		} else if ("Group".equals(type)) {
			strResult = ((Group)obj).getName();
		} else if ("Company".equals(type)) {
			strResult = ((Company)obj).getName();
		} else if ("Favorites".equals(type)) {
			strResult = ((Favorites)obj).getName();
		} else {
			Log.e("ContactsPls" , "[ListUtils] Error(getName) : type mismatching : '" + type + "'");
		}
		
		return strResult;
	}

	/**
	 * return object id
	 * 
	 * @param obj
	 * @param type
	 * @return
	 */
	static private String getID(Object obj, String type) {
		
		String strResult = "";
	
		if ("Contact".equals(type)) {
			strResult = ((Contact)obj).getID();
		} else if ("Group".equals(type)) {
			strResult = ((Group)obj).getID();
		} else if ("Company".equals(type)) {
			strResult = ((Company)obj).getID();
		} else if ("Favorites".equals(type)) {
			strResult = ((Favorites)obj).getID();
		} else {
			Log.e("ContactsPlus", "[ListUtils] Error(getID) : type mismatching : '" + type + "'");
		}
		
		return strResult;
	}

	/**
	 * return show property value
	 * 
	 * @param obj
	 * @param type
	 * @return
	 */
	static private boolean getShow(Object obj, String type) {
		
		boolean bResult = false;
	
		if ("Contact".equals(type)) {
			bResult = ((Contact)obj).getShow();
		} else if ("Group".equals(type)) {
//			bResult = ((Group)obj).getShow();
		} else if ("Company".equals(type)) {
//			bResult = ((Company)obj).getShow();
		} else if ("Favorites".equals(type)) {
			bResult = ((Favorites)obj).getShow();
		} else {
			Log.e("ContactsPlus", "[ListUtils] Error(getShow) : type mismatching : '" + type + "'");
		}
		
		return bResult;
	}

	/**
	 * Change all object viewing status.
	 * 
	 * ADD : version 1.1
	 * 
	 * @param list
	 * @param position
	 * @param show true : visible, false : invisible
	 * @param type object type (Contact, Company, Group)
	 * @return
	 */
	static private ArrayList setShow(ArrayList list, int position, boolean show, String type) {
		
		if ("Contact".equals(type)) {
			Contact contact = (Contact)list.get(position);
			contact.setShow(show);
			list.set(position, contact);
		} else if ("Group".equals(type)) {
			// do not have properties (ver 1.1)
//			Group contact = (Contact)list.get(position);
//			contact.setShow(show);
//			list.set(position, contact);
		} else if ("Company".equals(type)) {
			// do not have properties (ver 1.1)
//			Contact contact = (Contact)list.get(position);
//			contact.setShow(show);
//			list.set(position, contact);
		} else if ("Favorites".equals(type)) {
				Favorites favorites = (Favorites)list.get(position);
				favorites.setShow(show);
				list.set(position, favorites);
		} else {
			Log.e("ContactsPlus", "[ListUtils] Error(setShow) : type mismatching : '" + type + "'");
		}
		
		return list;
	}

	/**
	 * Sort Alphabet list (international version) 
	 * (Bubble sort)
	 * 
	 * @param list (ArrayList)
	 * @param language 
	 * @param type
	 * @return list
	 */
	static private ArrayList sortListInternational_bubble(ArrayList list, String language, String type) {
		
		Collator col = Collator.getInstance();
		Object tmpObj;
		
		if (list.size() == 1) return list;
		
		for (int i = 0; i < list.size(); i++) {
			for (int j = i + 1; j < list.size(); j++) {
				if( col.compare(getName(list.get(i), type), getName(list.get(j), type) ) > 0 ) {
					tmpObj = list.get(i);
					list.set(i, list.get(j));
					list.set(j, tmpObj);
				}
			}
		}
		
		return list;
	}

	static private ArrayList sortListInternational_quick(ArrayList list, String language, String type, int left, int right) {

		Collator col = Collator.getInstance();

		int i,j;
		Object tmp;
		Object p;
 
		if(left<right)  {
			i=left;
			j=right;
			p=list.get(left);
      
			//분할 과정
			while(i<j) {
				while( col.compare(getName(list.get(j), type), getName(p, type)) > 0) {
					j--;
				}
				while(i<j && col.compare(getName(list.get(i), type), getName(p, type)) <= 0) {
					i++;
				}
 
				tmp = list.get(i);
				list.set(i, list.get(j));
				list.set(j, tmp);
			}
			
			list.set(left, list.get(i));
			list.set(i, p);
			
			//정렬 과정
			sortListInternational_quick(list, language, type, left, i-1);
			sortListInternational_quick(list, language, type, i+1,right);   
		}
		
		return list;
	}
	
	/**
	 * Sort. just ContactsPlus style.
	 * PATTERN : ABCZABCDEFGZ (tow sorted group)
	 * 
	 * @param list
	 * @param language
	 * @param type
	 * @return
	 */
	static private ArrayList sortListInternational_two(ArrayList list, String language, String type) {
		
		Collator col = Collator.getInstance();
		
		ArrayList resultList = new ArrayList();

		// Found first position
		int iFirst = 0;
		for (int i=0; i<list.size(); i++) {
			
			if (i == list.size()-1) {
				iFirst = i+1;
				break;
			}
			
			if (col.compare(getName(list.get(i), type), getName(list.get(i+1), type)) > 0) {
				iFirst = i+1;
				break;
			}
		}
		
		if (iFirst == list.size()) {
			return list;
		}
		
		int iMove = iFirst;
		for (int i=0; i<iFirst; i++) {
			for (int j=iMove; j<list.size(); j++) {
				if (col.compare(getName(list.get(i), type), getName(list.get(j), type)) <= 0) {
					resultList.add(list.get(i));
					break;
				} else {
					resultList.add(list.get(j));
					iMove = j+1;
				}
			}
			if (iMove >= list.size() && i<iFirst) {
				resultList.add(list.get(i));
			}
		}
		
		if (iMove < list.size()) {
			for(int i=iMove; i<list.size(); i++) {
				resultList.add(list.get(i));
			}
		}
				
		return resultList;
	}

	/**
	 * Sort list 
	 * don't use this function (by ahnchan, 2011/05/02)
	 * it's my stupid algorithm. more research & thinking!
	 * 
	 * @param list
	 * @param language
	 * @param type
	 * @return
	 */
	static public ArrayList sortListInternational_OLD(ArrayList list, String language, String type) {

		ArrayList resultList = new ArrayList();
		String[] sortChar;
		
		if (list.size() <= 0) return list;
		
		// International Selection
		if ("cs".equals(language)) {
			// Czech 20110316
			sortChar = czech;
		} else if ("sv".equals(language)) {
			// Swedish 20110318
			sortChar = swedish;
		} else {
			return list;
		}

		Log.d("ListUtils", "Language:"+language);
		
		// Check contact data
		ArrayList<Boolean> ckList = new ArrayList<Boolean>(list.size());
		for(int i=0; i<list.size(); i++) {
			ckList.add(Boolean.FALSE);
		}

		// Sorting 
		for (int i=0; i<sortChar.length; i++) {
			
//			char cWord = sortChar[i];
			
			for (int j=0; j<list.size(); j++) {

				if (ckList.get(j).booleanValue()) {
					continue;
				}				
			
				if (asFirst(sortChar[i], getName(list.get(j), type))) {
					ckList.set(j, Boolean.TRUE);
					resultList.add(list.get(j));
				}
			}
		}
		
		// Not selected word
		for (int i=0; i<ckList.size(); i++) {
			if (Boolean.FALSE == ckList.get(i)) {
				resultList.add(list.get(i));
			}
		}
		
		return resultList;
	}
	
	/**
	 * compare first character. to find same first character 
	 * 
	 * @param first
	 * @param name
	 * @return true : same first character, false : different first character
	 */
	static boolean asFirst(String first, String name) {
		
		boolean bResult = false;
		
		if (null == first) return false;
		if (null == name) return false;
		if (first.length() <= 0) return false;
		if (name.length() <= 0) return false;
		
		if(first.equals(name.substring(0, first.length()))) {
			bResult = true;
		}
		
		return bResult;
	}
}
